import { generateNonceStr, generateSignature } from './encrypt'
import { useUserStore } from '@/stores/user'

// 请求配置接口
interface RequestConfig {
  timeout?: number
  retry?: number
  retryDelay?: number
}

// 默认配置
const DEFAULT_CONFIG: RequestConfig = {
  timeout: 10000, // 10秒超时
  retry: 3, // 最多重试3次
  retryDelay: 1000, // 重试间隔1秒
}

// 错误码枚举
export enum ErrorCode {
  SUCCESS = 0,
  UNAUTHORIZED = 401,
  FORBIDDEN = 403,
  NOT_FOUND = 404,
  INTERNAL_ERROR = 500,
  NETWORK_ERROR = 1000,
  TIMEOUT_ERROR = 1001,
  UNKNOWN_ERROR = 9999,
}

// 错误信息映射
const ERROR_MESSAGES: Record<ErrorCode, string> = {
  [ErrorCode.SUCCESS]: '操作成功',
  [ErrorCode.UNAUTHORIZED]: '未授权，请重新登录',
  [ErrorCode.FORBIDDEN]: '没有权限访问',
  [ErrorCode.NOT_FOUND]: '请求的资源不存在',
  [ErrorCode.INTERNAL_ERROR]: '服务器内部错误',
  [ErrorCode.NETWORK_ERROR]: '网络连接失败',
  [ErrorCode.TIMEOUT_ERROR]: '请求超时',
  [ErrorCode.UNKNOWN_ERROR]: '未知错误',
}

// 自定义错误类
export class RequestError extends Error {
  code: ErrorCode
  data?: any

  constructor(code: ErrorCode, message?: string, data?: any) {
    super(message || ERROR_MESSAGES[code])
    this.code = code
    this.data = data
    this.name = 'RequestError'
  }
}

// 响应数据结构
interface EchoResponse<T = any> {
  success: boolean
  result: T
  code: number
  message: string
  timestamp?: number
}

export interface RequestOptions extends UniApp.RequestOptions {
  loading?: boolean
  auth?: boolean
  custom?: Record<string, any>
  responseType?: 'text' | 'arraybuffer'
  ignoreCancelToken?: boolean
  ignoreSignature?: boolean // 是否忽略签名
}

// 配置基础URL
const baseURL = import.meta.env.VITE_API_URL || ''

// 请求队列
const requestQueue: UniApp.RequestTask[] = []

// 创建请求实例
class Request {
  private static instance: Request
  private constructor() {}

  static getInstance() {
    if (!Request.instance) {
      Request.instance = new Request()
    }
    return Request.instance
  }

  // 请求拦截器
  private beforeRequest(options: RequestOptions) {
    const userStore = useUserStore()

    // 处理 URL
    options.url = `${baseURL}${options.url}`

    // 处理 header
    const timestamp = Date.now()
    const nonceStr = generateNonceStr()

    options.header = {
      ...options.header,
      'Content-Type': 'application/json',
      'X-Access-Token': userStore.token || '',
      'X-TIMESTAMP': timestamp,
      'X-SIGN': '',
      'X-NONCE-STR': nonceStr,
    }

    // 生成签名
    if (!options.ignoreSignature) {
      let params: Record<string, any> = {}

      // 处理 GET 请求参数
      if (options.method === 'GET' && options.data) {
        params = typeof options.data === 'object' ? { ...options.data } : {}
      }
      // 处理 POST/PUT/DELETE 请求数据
      else if (options.data && typeof options.data === 'object') {
        params = { ...options.data }
      }

      const sign = generateSignature(params, timestamp)
      options.header['X-SIGN'] = sign
    }

    // 显示加载提示
    if (options.loading !== false) {
      uni.showLoading({ title: '加载中...', mask: true })
    }

    return options
  }

  // 响应拦截器
  private handleResponse<T>(
    response: UniApp.RequestSuccessCallbackResult,
    resolve: (value: T | PromiseLike<T>) => void,
    reject: (reason?: any) => void,
    options: RequestOptions,
  ) {
    const res = response.data as EchoResponse<T>

    // 隐藏加载提示
    if (options.loading !== false) {
      uni.hideLoading()
    }

    // 处理业务状态码
    if (res.success) {
      resolve(res.result)
      return
    }

    // 处理特定错误码
    switch (res.code) {
      case 401:
        this.handleAuthError()
        reject(new Error(res.message || ERROR_MESSAGES[401]))
        break
      case 403:
        uni.showToast({
          title: ERROR_MESSAGES[403],
          icon: 'none',
          duration: 2000,
        })
        reject(new Error(res.message || ERROR_MESSAGES[403]))
        break
      default:
        uni.showToast({
          title: res.message || '操作失败',
          icon: 'none',
          duration: 2000,
        })
        reject(new Error(res.message || '操作失败'))
    }
  }

  // 处理认证错误
  private handleAuthError() {
    const userStore = useUserStore()
    userStore.logout()

    // 清除所有请求
    this.cancelAllRequests()

    // 跳转登录页
    const currentPage = getCurrentPages().pop()
    const redirectUrl = currentPage ? encodeURIComponent(currentPage.route as string) : ''

    uni.reLaunch({
      url: `/pages/login/index?redirect=${redirectUrl}`,
    })
  }

  // 错误处理
  private handleError(error: any, options: RequestOptions) {
    if (options.loading !== false) {
      uni.hideLoading()
    }

    let message = '网络异常，请稍后重试'
    if (error.errMsg) {
      if (error.errMsg.includes('timeout')) {
        message = '请求超时，请稍后重试'
      }
      else if (error.errMsg.includes('fail')) {
        message = '网络连接失败，请检查网络'
      }
    }

    uni.showToast({
      title: message,
      icon: 'none',
      duration: 2000,
    })

    return Promise.reject(error)
  }

  // 发送请求
  request<T = any>(options: RequestOptions): Promise<T> {
    options = this.beforeRequest(options)

    return new Promise((resolve, reject) => {
      const requestTask = uni.request({
        ...options,
        timeout: 15000, // 15秒超时
        success: (response) => {
          this.handleResponse<T>(response, resolve, reject, options)
        },
        fail: (error) => {
          this.handleError(error, options)
        },
        complete: () => {
          const index = requestQueue.findIndex(task => task === requestTask)
          if (index > -1) {
            requestQueue.splice(index, 1)
          }
        },
      })

      requestQueue.push(requestTask)
    })
  }

  // 取消所有请求
  cancelAllRequests() {
    requestQueue.forEach(task => task.abort())
    requestQueue.length = 0
  }

  // 封装常用请求方法
  get<T = any>(url: string, params?: Record<string, any>, config: Omit<RequestOptions, 'url' | 'method' | 'data'> = {}) {
    return this.request<T>({
      url,
      data: params,
      method: 'GET',
      ...config,
    })
  }

  post<T = any>(url: string, data?: any, config: Omit<RequestOptions, 'url' | 'method' | 'data'> = {}) {
    return this.request<T>({
      url,
      data,
      method: 'POST',
      ...config,
    })
  }

  put<T = any>(url: string, data?: any, config: Omit<RequestOptions, 'url' | 'method' | 'data'> = {}) {
    return this.request<T>({
      url,
      data,
      method: 'PUT',
      ...config,
    })
  }

  delete<T = any>(url: string, data?: any, config: Omit<RequestOptions, 'url' | 'method' | 'data'> = {}) {
    return this.request<T>({
      url,
      data,
      method: 'DELETE',
      ...config,
    })
  }

  // 下载文件
  download(url: string, params?: Record<string, any>) {
    const timestamp = Date.now()
    const nonceStr = generateNonceStr()
    const sign = generateSignature(params || {}, timestamp)

    return new Promise((resolve, reject) => {
      uni.downloadFile({
        url: this.buildUrl(`${baseURL}${url}`, params),
        header: {
          'X-Access-Token': useUserStore().token || '',
          'X-TIMESTAMP': timestamp,
          'X-SIGN': sign,
          'X-NONCE-STR': nonceStr,
        },
        success: (res) => {
          if (res.statusCode === 200) {
            resolve(res.tempFilePath)
          }
          else {
            reject(new Error('下载失败'))
          }
        },
        fail: (error) => {
          reject(error)
        },
      })
    })
  }

  // 上传文件
  upload(url: string, filePath: string, formData?: Record<string, any>) {
    const timestamp = Date.now()
    const nonceStr = generateNonceStr()
    const sign = generateSignature(formData || {}, timestamp)

    return new Promise((resolve, reject) => {
      uni.uploadFile({
        url: `${baseURL}${url}`,
        filePath,
        name: 'file',
        formData,
        header: {
          'X-Access-Token': useUserStore().token || '',
          'X-TIMESTAMP': timestamp,
          'X-SIGN': sign,
          'X-NONCE-STR': nonceStr,
        },
        success: (res) => {
          if (res.statusCode === 200) {
            try {
              const data = JSON.parse(res.data)
              resolve(data)
            }
            catch (e) {
              console.error('上传失败:', e)
              reject(new Error('上传失败'))
            }
          }
          else {
            reject(new Error('上传失败'))
          }
        },
        fail: (error) => {
          reject(error)
        },
      })
    })
  }

  // 构建 URL 参数
  private buildUrl(url: string, params?: Record<string, any>): string {
    if (!params)
      return url
    const queryString = Object.keys(params)
      .filter(key => params[key] != null && params[key] !== '')
      .map(key => `${encodeURIComponent(key)}=${encodeURIComponent(params[key])}`)
      .join('&')
    return queryString ? `${url}?${queryString}` : url
  }
}

export const request = Request.getInstance()
